import Axios, { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig } from "axios";
import qs from "qs";
import {
  HttpClientConfig,
  HttpRequestConfig,
  HttpResponse,
  HttpError,
  RequestMethods
} from "./types";
import { logRequestMsg } from "./log.uils";
export class UniversalHttpClient {
  private axiosInstance: AxiosInstance;
  private config: HttpClientConfig;

  constructor(config: HttpClientConfig = {}) {
    this.config = {
      timeout: 10000,
      headers: {
        Accept: "application/json, text/plain, */*",
        "Content-Type": "application/json",
        "X-Requested-With": "XMLHttpRequest"
      },
      ...config
    };

    // 创建axios实例
    this.axiosInstance = Axios.create({
      baseURL: this.config.baseURL,
      timeout: this.config.timeout,
      headers: this.config.headers,
      // 数组格式参数序列化
      paramsSerializer: params => qs.stringify(params, { indices: false })
    });

    this.setupInterceptors();
  }

  private buildLoadingCode(config?: HttpRequestConfig): string {
    if (config) {
      const method = config.method;
      const url = config.url;
      const loadingCode = config.loadingCode;
      return loadingCode || `${method}${url}`;
    }
    return '';
  }

  private setupInterceptors(): void {
    // 请求拦截器
    this.axiosInstance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        // 启动loading
        if (this.config.loadingHandler) {
          this.config.loadingHandler.start(this.buildLoadingCode(config));
        }

        // 执行请求回调
        const httpConfig = config as HttpRequestConfig;
        if (typeof httpConfig.beforeRequestCallback === "function") {
          httpConfig.beforeRequestCallback(httpConfig);
        } else if (this.config.beforeRequest) {
          this.config.beforeRequest(httpConfig);
        }

        // 添加token
        if (this.config.getToken) {
          const token = this.config.getToken();
          if (token && config.headers) {
            config.headers["authorization"] = token;
          }
        }

        return config;
      },
      error => {
        // 结束loading
        if (this.config.loadingHandler) {
          this.config.loadingHandler.end(this.buildLoadingCode(error.config));
        }
        return Promise.reject(error);
      }
    );

    // 响应拦截器
    this.axiosInstance.interceptors.response.use(
      (response: HttpResponse) => {
        // 结束loading
        if (this.config.loadingHandler) {
          this.config.loadingHandler.end(this.buildLoadingCode(response.config));
        }

        // 执行响应回调
        if (typeof response.config.beforeResponseCallback === "function") {
          response.config.beforeResponseCallback(response);
        } else if (this.config.beforeResponse) {
          this.config.beforeResponse(response);
        }

        return response.data;
      },
      (error: HttpError) => {
        error.isCancelRequest = Axios.isCancel(error);
        
        // 结束loading
        if (this.config.loadingHandler) {
          this.config.loadingHandler.end(this.buildLoadingCode(error.config));
        }

        // 执行错误处理
        if (this.config.errorHandler) {
          const result = this.config.errorHandler(error);
          // 如果errorHandler返回了值（包括undefined），则返回这个值
          // 这样用户就可以通过返回resolved Promise或普通值来改变错误处理的结果
          if (result !== undefined) {
            if (result instanceof Promise) {
              return result;
            }
            // 如果返回的是Error对象或普通值，则reject
            return Promise.reject(result);
          }
        }
       // 有返回响应数据【err.response.data】才处理
        if (error && error.response && Object.prototype.hasOwnProperty.call(error.response, 'data')) {
          const errorMessage = logRequestMsg(error.response)
          const errData = error.response.data
          // 如果需要错误信息
          const httpConfig = error.config as HttpRequestConfig;
          if (httpConfig && httpConfig.needErrorCode) {
            // 确保 errData 是一个对象，可以安全地添加 message 属性
            const errorDataWithMessage = typeof errData === 'object' && errData !== null ? { ...errData } : {};
            (errorDataWithMessage as any).message = errorMessage;
            return Promise.reject(errorDataWithMessage);
          }
          return Promise.reject(errorMessage)
        }
        return Promise.reject('服务器出错，请重试');
      }
    );
  }

  // 通用请求方法
  public request<T = any>(
    method: RequestMethods,
    url: string,
    param?: AxiosRequestConfig,
    axiosConfig?: HttpRequestConfig
  ): Promise<T> {
    const config = {
      method,
      url,
      ...param,
      ...axiosConfig
    } as HttpRequestConfig;

    return new Promise((resolve, reject) => {
      this.axiosInstance
        .request(config)
        .then((response) => {
          resolve(response as T);
        })
        .catch(error => {
          reject(error);
        });
    });
  }

  // GET请求
  public get<T = any>(
    url: string,
    params?: AxiosRequestConfig,
    config?: HttpRequestConfig
  ): Promise<T> {
    return this.request<T>("get", url, params, config);
  }

  // POST请求
  public post<T = any>(
    url: string,
    params?: AxiosRequestConfig,
    config?: HttpRequestConfig
  ): Promise<T> {
    return this.request<T>("post", url, params, config);
  }

  // PUT请求
  public put<T = any>(
    url: string,
    params?: AxiosRequestConfig,
    config?: HttpRequestConfig
  ): Promise<T> {
    return this.request<T>("put", url, params, config);
  }

  // DELETE请求
  public delete<T = any>(
    url: string,
    params?: AxiosRequestConfig,
    config?: HttpRequestConfig
  ): Promise<T> {
    return this.request<T>("delete", url, params, config);
  }

  // PATCH请求
  public patch<T = any>(
    url: string,
    params?: AxiosRequestConfig,
    config?: HttpRequestConfig
  ): Promise<T> {
    return this.request<T>("patch", url, params, config);
  }

  // 更新配置
  public updateConfig(config: Partial<HttpClientConfig>): void {
    this.config = { ...this.config, ...config };
    
    // 更新axios实例配置
    if (config.baseURL) {
      this.axiosInstance.defaults.baseURL = config.baseURL;
    }
    if (config.timeout) {
      this.axiosInstance.defaults.timeout = config.timeout;
    }
    if (config.headers) {
      this.axiosInstance.defaults.headers = { ...this.axiosInstance.defaults.headers, ...config.headers };
    }
  }
}